iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0
自我挑戰組

愛安豬系列 第 4

# oops

  • 分享至 

  • xImage
  •  

Day 4:KMP 的 Gradle——version catalog、依賴、判斷 library 支不支援 KMP

可惜了,不過,繼續

KMP 的 Gradle

Android 的 build.gradle.kts 大部分時間只改 dependencies { },其他 block 少有動過。

KMP 不一樣。它的 build 檔同時描述多個 compile target,依賴的宣告位置直接決定 code 能不能看見那個 library。放錯 source set,錯誤訊息不會說「你放錯地方」,只會說 Unresolved reference,跟 Day 3 的 probe 一模一樣。所以今天的目的是:以後看到 unresolved reference 時,第一個懷疑的是依賴位置,不是拼字。

1. 先看 wizard 給的 build.gradle.kts 是怎麼組織依賴的

kotlin {
    androidTarget { ... }
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3)
            implementation(libs.androidx.lifecycle.viewmodel)
            implementation(libs.androidx.lifecycle.runtime.compose)
        }
        androidMain.dependencies {
            implementation(compose.preview)
            implementation(libs.androidx.activity.compose)
        }
        // iosMain.dependencies 目前是空的
    }
}

三個 點:

androidx.lifecycle.viewmodel 在 commonMain。 這不是筆誤。Jetpack 的 lifecycle、viewmodel、navigation 這幾個 library 從 2024 開始發 KMP artifact,可以在 commonMain 用。Day 1 說「Android 那套 stack 大部分能搬」指的就是這件事。

activity.compose 在 androidMain。 因為 ComponentActivity 是 Android 的東西,iOS 沒有 Activity。

compose.runtime 用的是 compose. 前綴,不是 libs. 這是 Compose Multiplatform Gradle plugin 提供的 accessor,它會自動對到跟 plugin 版本相容的 artifact,省掉自己對版本。Android 那邊的 androidx.compose.* 不用另外宣告,CMP plugin 在 Android target 會自動換成 Google 發的 artifact。

2. 依賴放哪的判斷規則

跟 Day 3 的可見性表是同一件事,只是換成 library:

library 的性質 放在 例子
有發 KMP artifact,邏輯要共用 commonMain.dependencies Ktor client core、kotlinx.serialization、Room KMP、Koin
只有 JVM/Android 版 androidMain.dependencies OkHttp、Ktor 的 OkHttp engine、Activity Compose
只有 iOS 版 iosMain.dependencies Ktor 的 Darwin engine
兩邊實作不同但介面相同 common 放介面 artifact,各平台放 engine Ktor:core 在 common,engine 分平台

Ktor 是最典型的例子,它的 ktor-client-core 是 multiplatform,但底層 HTTP engine 每個平台不同:Android 用 OkHttp,iOS 用 Darwin(NSURLSession)。這正是 Day 3 講的 dependsOn 結構在 library 層的反映。

3. 怎麼判斷一個 library 有沒有支援 KMP

這是我今天最想解決的問題,因為 AI 在這件事上特別不可靠——它會很有信心地說某個 library「支援 KMP」,但那可能是三個版本前的狀態,或者根本是幻覺。

三個可靠的方法:

看 Maven Central 的 artifact 列表。 KMP library 會為每個 target 發一個 artifact。以 Ktor 為例,搜 io.ktor:ktor-client-core,會看到:

ktor-client-core            ← Gradle metadata 的入口
ktor-client-core-jvm
ktor-client-core-iosarm64
ktor-client-core-iossimulatorarm64
ktor-client-core-iosx64
ktor-client-core-js
...

-iosarm64 就代表它能進 commonMain 並且在 iOS 上編。沒有的話,就只能放 androidMain。

看 klibs.io。 JetBrains 做的 KMP library 索引,可以直接篩選 target。比 GitHub README 可信,因為它是從實際發布的 artifact 掃出來的。

讓 Gradle 告訴你。 把 library 放進 commonMain,sync。如果它沒有 iOS artifact,錯誤訊息會直接說:

Could not resolve io.some:library:1.0.
  No matching variant of io.some:library:1.0 was found.
  The consumer was configured to find ... 'org.jetbrains.kotlin.platform.type' with value 'native' ...

看到 platform.typenative 這兩個字,就是「這個 library 沒有 iOS 版」。

4. Version catalog 在 KMP 的整理方式

gradle/libs.versions.toml 跟 Android 一樣,但我做了一個分組習慣,讓日後一眼看出哪些是 common、哪些是平台專屬:

[versions]
kotlin = "2.4.x"
compose-multiplatform = "1.11.x"
agp = "9.x"
ktor = "3.x"
# 以你 sync 時 wizard 給的為準

[libraries]
# --- common (KMP) ---
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }

# --- android only ---
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }

# --- ios only ---
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }

用註解分三區。不是 Gradle 要求,是給三週後的自己看的。

另外,Kotlin plugin 和 Compose compiler plugin 的版本必須一致——這不是 catalog 裡兩行寫一樣就好,而是要用同一個 version.ref

[plugins]
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

wizard 已經幫你這樣寫了,升級時不要手動改成兩個不同的數字。

5. 今天問 AI 什麼

今天的實驗是測 AI 的「幻覺率」。我列了五個 Android developer 常用的 library,問它哪些支援 KMP:

For each of these libraries, tell me whether the current version publishes
Kotlin Multiplatform artifacts for iOS (iosArm64), and if not, what the
KMP-compatible alternative is:
1. Retrofit
2. Moshi
3. Room
4. Coil
5. Timber

它的答案對照 Maven Central 之後:

library AI 說 實際
Retrofit 不支援,改用 Ktor ✅ 正確
Moshi 不支援,改用 kotlinx.serialization ✅ 正確
Room 支援(2.7+) ✅ 正確
Coil 支援(3.x) ✅ 正確
Timber 不支援,改用 Napier 或 Kermit ✅ 正確,但沒提 Kermit 和 Napier 的維護狀態差異

這幾個 library 的 KMP 狀態在 2024–2025 就穩定了。AI 在「已經穩定一年以上的事實」上可信度高,在「最近半年變動的東西」上要查。 這條放進 Phase 5。

6. Commit

git commit -am "chore: reorganize version catalog by common/android/ios (AI: none)"

Phase 1 回顧

四天下來寫的 Kotlin code 是零行,但拿到了三個以後每天都會用的工具:

  1. 可見性規則(Day 3):commonMain 只看得見兩邊都有的東西。
  2. compiler 驗證(Day 3):寫一行、看紅線、搬資料夾。
  3. artifact 判斷(Day 4):有 -iosarm64 才能進 commonMain。

再加上一個對 AI 的校準:專案設定和最新版本的問題要查,穩定超過一年的事實可以信,「官方推薦」四個字要特別警覺。

明天

Phase 2 開始。Day 5 正面處理 Day 3 留下的問題:expect/actual 和 interface 到底什麼時候用哪個。 會用 KMP Reader 真正需要的第一個平台能力——log——當例子,兩種寫法都做一次,然後看哪一種在寫 test 時比較不痛苦。


上一篇
# meow meow
系列文
愛安豬4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言